#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define BUFF_SIZE 256

int verbose = 0;

// Questa funzione calcola la rappresentativita' di una sottostruttura rispetto ad un insieme di grafi
float represgiso(unsigned long int current_sub, unsigned int ngraph, unsigned int occurrence[][ngraph], char* exp_path, char * subdue_binpath);

// Questa funzione estrae dal file .subs le sottostrutture di primo livello.
void representativeness(char* filename_subs, char* filename_g, char* filename_csv, char* exp_path, char *subdue_binpath);

// Questo programma necessita dei seguenti parametri:
// - "-v" (opzionale) per abilitare la modalita' verbose
// - il file .g (obbligatorio)
// - il file .subs (obbligatorio)
int main ( int argc, char* argv[] )
{
	// Queste sono le variabili che conterranno i percorsi dei files dati in input
	char file_g[BUFF_SIZE], file_subs[BUFF_SIZE], file_csv[BUFF_SIZE], exp_path[BUFF_SIZE], subdue_binpath[BUFF_SIZE], create_dir[BUFF_SIZE];

	// Viene salvato il tempo di calendario all'inizio dell'esecuzione per poterne calcolare la durata
	time_t inizio = time(NULL);

	// Se vengono passati 4 parametri tra cui "-v" viene attivata tale modalita'
	if (argc == 4 && !strcmp(argv[1],"-v"))
		verbose=1;
	else if(argc != 3 )
	{
		fprintf(stderr, "Usage: %s [-v] <file.g> <file.g.cmp>", argv[0]);
		exit(EXIT_FAILURE);
	}

	// Vengono settati i percorsi agendo sulle stringhe
	strcpy(file_g, argv[verbose+1]);
	strcpy(file_subs, argv[verbose+2]);
	strcpy(exp_path, argv[verbose+2]);
	char *lastslash = strrchr(exp_path, '/');
	if (lastslash)
		exp_path[(int)(lastslash - exp_path)] = '\0';
	else
	{
		strcpy(exp_path, ".");
		sprintf(file_g, "./%s", argv[verbose+1]);
		sprintf(file_subs, "./%s", argv[verbose+2]);
	}
	strcpy(subdue_binpath, argv[0]);
	lastslash = strrchr(subdue_binpath, '/');
	if (lastslash)
		subdue_binpath[(int)(lastslash - subdue_binpath)] = '\0';
	else
		strcpy(subdue_binpath, ".");

	// Vengono create (se non esistono) le cartelle per contenere i log ed i files temporanei
	sprintf(create_dir, "mkdir -p %s/{temp,log}", exp_path);
	system(create_dir);
	sprintf(file_csv, "%s/log/representativeness.csv", exp_path);
	sprintf(exp_path, "%s/temp", exp_path);


	// Viene effettivamente lanciata la rappresentativita'
	representativeness(file_subs, file_g, file_csv, exp_path, subdue_binpath);

	// Viene salvato il tempo di calendario alla fine dell'esecuzione
	time_t fine = time(NULL);

	// Viene scritta la durata dell'esecuzione
	printf("\nIl calcolo della rappresentativita' ha richiesto circa %d secondi\n", (int)difftime(fine, inizio));

	return 0;
}

// Questa funzione estrae dal file .subs le sottostrutture di primo livello. Per ciascuna sottrostruttura
// viene lanciata la funzione representativeness per calcolarne la rappresentativita' rispetto ai grafi
// contenuti nel file .g passato come parametro
void representativeness(char* filename_subs, char* filename_g, char* filename_csv, char* exp_path, char *subdue_binpath)
{
	// Viene aperto il file contenente i grafi
	FILE *file_g = fopen ( filename_g, "r" );

	// Questa variabile conta il numero di grafi rispetto a cui calcolare la rappresentativita'
	unsigned int ngraph = 1;

	// Questa variabile individua se il primo grafo e' stato considerato (xp == 1)
	// o meno (xp == 0). In questo caso il file .g contiene un solo grafo
	int xp = 0;

	// Viene effettuata la creazione di un file temporaneo per contenere il primo grafo.
	// Tale creazione avviene al di fuori del ciclo while in modo da considerare il caso
	// in cui ci sia un solo grafo (e dunque nessun "XP")
	char buffer[BUFF_SIZE];
	sprintf(buffer, "%s/GRAPH_1.g", exp_path);
	FILE* file_xp = fopen(buffer, "w+");
	char filename_xp[BUFF_SIZE];


	// Questo ciclo estrae i grafi dal file .g corrispondente e li inserisce
	// uno ad uno in files temporanei
	while (fgets(buffer, sizeof(buffer), file_g))
	{
		// Quando il primo carattere di una riga del file .g e' 'X' siamo in presenza di un nuovo grafo
		if (buffer[0] == 'X' && buffer[1] == 'P')
		{
			// Se si e' gia' esaminato uno o piu' grafi e viene trovata un ulteriore 'X' deve essere creato
			// un file temporaneo per contenere il nuovo grafo
			if(ngraph!=1 || xp)
			{
				// I grafi vengono inseriti nei files GRAPH_x.g dove x e' il numero del grafo considerato
				sprintf(filename_xp, "%s/GRAPH_%d.g", exp_path, ++ngraph);
				if(file_xp) fclose(file_xp);
                file_xp = fopen(filename_xp, "w+");
			}
			else xp=1;
		}
		else
		{
			// Vengono copiate nel file GRAPH_x.g le righe corrispondenti al grafo considerato eccetto
			// le righe vuote o quelle relative a commenti (denotati dal carattere '%', il cui
			// codice ASCII e' 37)
			if (buffer[0] != '\n' && buffer[0] != 37)
				fputs(buffer, file_xp);
		}
	}
	if (file_g)
		fclose(file_g);

	if (file_xp)
			fclose(file_xp);

	// Viene creata la struttura dati per memorizzare il numero progressivo
	// delle sottostrutture di primo livello
	typedef struct TopSubs{
		unsigned long int sub;
		struct TopSubs * next;
	} TopSubs;

	TopSubs *topsubs = NULL, *current_topsub;
	unsigned long int current_sub = 0, ntopsubs = 0;

	// Viene aperto il file su cui scrivere le occorrenze di
	// ogni sottostruttura sui vari grafi
	FILE* file_occurrence = fopen(filename_csv,"w+");

	if (!file_occurrence)
	{
		fprintf(stderr, "\nImpossibile creare representativeness.csv\n");
		exit(EXIT_FAILURE);
	}
	else
	{
		fputs("Sub,Occurrences\n", file_occurrence);
	}
	// Ed il file contenente le sottostrutture
	FILE* file_subs = fopen(filename_subs, "r");

	if (file_subs)
	{
		if (verbose)
			printf("Il file %s e' stato caricato\n", strrchr(filename_subs,'/')+1);

		// Questo ciclo permette di estrarre le sottostrutture di primo livello
		// inserite da subdue nel file .subs
		// Contestualmente vengono creati i files TOPSUB_x.g contenenti
		// i grafi relativi ad ogni sottostruttura considerata

		char filename_currentsub[BUFF_SIZE];

		while (fgets(buffer, sizeof(buffer), file_subs))
		{
			if (buffer[0] == 'S' && buffer[1] == '\n')
			{
				current_sub++;
				fgets(buffer, sizeof(buffer), file_subs);
				int z=0;
				while (buffer[z] && buffer[z] != 'S')
					z++;

				// Questa condizione consente di individuare le sottostrutture di primo livello
				// cioe' quelle che non contengono altre sottostrutture
				if (!(buffer[z] == 'S' && buffer[z+1] == 'U' && buffer[z+2] == 'B' && buffer[z+3] == '_'))
				{
					ntopsubs++;
					if (topsubs == NULL)
					{
						topsubs = (TopSubs *)malloc(sizeof(TopSubs));
						current_topsub = topsubs;
					}
					else
					{
						current_topsub->next = (TopSubs *)malloc(sizeof(TopSubs));
						current_topsub = current_topsub->next;
					}
					current_topsub->sub = current_sub;
					current_topsub->next = NULL;

					// Viene creato e aperto il file TOPSUB_x.g per contenere le sottostrutture
					sprintf(filename_currentsub, "%s/TOPSUB_%lu.g", exp_path, current_sub);
					FILE* file_g = fopen(filename_currentsub, "w+");

					// Vengono copiate le righe della sottostruttura considerata dal file .subs
					// al file .g relativo alla stessa finche' non termina il file o non viene
					// trovata una riga vuota
					do
						fputs(buffer, file_g);
					while (fgets(buffer, sizeof(buffer), file_subs) && buffer[0] != '\n');

					fclose(file_g);
				}
			}
		}

		unsigned int occurrence[ntopsubs][ngraph];

		// Viene scorsa la lista di sottortrutture di primo livello
		current_topsub = topsubs;
		int i = 0;
		while (current_topsub)
		{
			// Viene calcolata la rappresentativita' per la sottostruttura considerata
			// con tutti i grafi trovati ed i risultati vengono stampati a schermo
			sprintf(filename_currentsub, "TOPSUB_%lu.g", current_topsub->sub);
			printf ("\nLa rappresentativita' di %s su %s e' del: %3.3f %%\n\n", filename_currentsub, strrchr(filename_g,'/')+1, represgiso(current_topsub->sub, ngraph, occurrence, exp_path, subdue_binpath)*100);
			// Viene creata una stringa atta a contenere una riga del file
			// con le occorrenze delle sottostrutture ed un puntatore a char
			// per scorrerla mentre la si scriver
			char occ_string[7*(ngraph+1)];
			char* occ_buffer = occ_string;

			// Viene effettuato un append sulla stringa con il valore corrente
			// letto dalla matrice delle occorrenze
			occ_buffer += sprintf(occ_buffer, "SUB_%lu,", current_topsub->sub);
			int j = 0;
			for (j=0; j<ngraph; j++)
				// I campi sono separati da una virgola, e viene riservato lo
				// spazio per 6  cifre decimali
				occ_buffer += sprintf(occ_buffer, "%d,", occurrence[i][j]);

			// Alla fine di ogni stringa viene inserito un carattere di fine riga
			// e cancellato l'ultimo spazio
			sprintf(occ_buffer-1, "\n");
			fputs(occ_string, file_occurrence);

			current_topsub = current_topsub->next;
			i++;
		}

		if (file_occurrence)
			fclose(file_occurrence);

	}
	else
		printf("Il file %s non e' stato caricato\n", filename_subs);

}

// Questa funzione calcola la rappresentativita' di una sottostruttura rispetto ad un insieme di grafi
// Sia la sottostruttura che i grafi considerati sono esaminati a partire dai files .g passati
// come parametri (subs e g rispettivamente).
// Il nome della funzione viene dal tool di subdue utilizzato in essa (SGISO appunto)
float represgiso(unsigned long int current_sub, unsigned int ngraph, unsigned int occurrence[][ngraph], char* exp_path, char *subdue_binpath)
{
	// Questa variabile indica l'indice nella tabella delle occorrenze che viene occupato
	// dalla corrente sottostruttura di primo livello presa in esame
	// Verra' incrementata ad ogni istanza di questa funzione
	static unsigned int index;

	char filename_xp[BUFF_SIZE], filename_subs[BUFF_SIZE];
	sprintf(filename_subs, "%s/TOPSUB_%lu.g", exp_path, current_sub);

	// Questa variabile indica il numero di grafo in cui viene cercata la sottostruttura subs
	int current_graph=0;

	// Questa variabile conta il numero di grafi in cui la sottostruttura subs e' presente
	float presence=0;

	// Questo ciclo verifica la presenza della sottostruttura subs nei grafi precedentemente estratti
	while (ngraph - current_graph++)
	{
		char buffer[BUFF_SIZE];
		sprintf(filename_xp, "%s/GRAPH_%d.g", exp_path, current_graph);

		// Comando da eseguire nella shell di sistema
		// Richiama il tool SGISO di subdue che conta il numero di istanze di una
		// sottostruttura presenti in un certo grafo e ne scansiona l'output alla ricerca
		// dell'informazione reindirizzate sul file aperto dalla "popen()"
		char sgiso_string[BUFF_SIZE];
		sprintf(sgiso_string, "%s/sgiso %s %s | grep Found | cut -d \" \" -f2", subdue_binpath, filename_subs, filename_xp);

		FILE *file_sgiso = NULL;
		file_sgiso = (FILE*)popen(sgiso_string, "r");
		if (file_sgiso)
		{
			fgets(buffer, sizeof(buffer), file_sgiso);
			pclose(file_sgiso);
			// Questa variabile contiene il numero di istanze della sottostruttura presenti
			// nel grafo esaminato
			int i = atoi(buffer);
			if (verbose) printf("La struttura %s e' presente %d %s nel grafo %d\n", strrchr(filename_subs, '/')+1, i, (i==1)?"volta":"volte", current_graph);
			occurrence[index][current_graph-1] = i;
			if(i!=0)
				presence++;
		}
		else
			printf("\nErrore nella creazione della \"pipe\"\n");
	}

	// Viene incrementato l'indice delle sottostrutture di primo livello
	// nella matrice delle occorrenze
	index++;

	// Viene ritornato il valore della rappresentativita' come rapporto tra numero di grafi
	// in cui e' presente la sottostruttura e numero di grafi totale
	return (float)presence/ngraph;
}
